
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/05/14 16:33:10
// Design Name: 
// Module Name: ddr_rw
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 写完一帧图像，按行输出到输出fifo，输出fifo读完后，再输出第二行到输出fifo，一直输出完一帧结束，改变掩码，开始写入第二帧图像
// 4:1速率，ddr3接口时钟400MHz，用户时钟100MHz
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module ddr3_rw(
    // system ---------------------------------------------------------------------
    input                   clk_200m            , // 200Mhz
    input                   sys_rst_n           , // 复位，低电平有效
    // ddr3 电路接口 ---------------------------------------------------------------
    inout       [15:0]      ddr3_dq             , // ddr3 数据线
    inout       [ 1:0]      ddr3_dqs_n          , // ddr3 数据选通负信号
    inout       [ 1:0]      ddr3_dqs_p          , // ddr3 数据选通正信号
    output      [13:0]      ddr3_addr           , // ddr3 地址线
    output      [ 2:0]      ddr3_ba             , // ddr3 Bank地址
    output                  ddr3_ras_n          , // ddr3 行地址选通信号，低电平有效
    output                  ddr3_cas_n          , // ddr3 列地址选通信号，低电平有效
    output                  ddr3_we_n           , // ddr3 写使能信号，低电平有效
    output                  ddr3_reset_n        , // ddr3 复位信号，低电平有效
    output                  ddr3_ck_p           , // ddr3 时钟正信号
    output                  ddr3_ck_n           , // ddr3 时钟负信号
    output                  ddr3_cke            , // ddr3 时钟使能
    output                  ddr3_cs_n           , // ddr3 片选信号，低电平有效
    output      [ 1:0]      ddr3_dm             , // ddr3 数据掩码
    output                  ddr3_odt            , // ddr3 输出驱动使能
    // ddr3 应用接口 ---------------------------------------------------------------
    output                  ui_clk              , // 用户时钟，这里是100MHz
    output                  init_calib_complete , // ddr3 初始化和校准完成信号  
    // 外部控制接口
    input       [ 24:0]     begin_addr          , // ddr 读写开始地址
    input                   rw                  , // ddr 读写标志，1读 0写
    input       [  1:0]     mask_switch         , // 00不遮掩 01高位有效 10低位有效
    input       [127:0]     wr_data             , // ddr 待写数据，接输入fifo
    input       [ 15:0]     burst_data_len      , // 突发长度
    input                   exc                 , // 执行标志
    output                  input_fifo_rd_en    , // 输入fifo读取使能
    output                  output_fifo_wr_en   , // 输出fifo写入使能
    output      [127:0]     rd_data             , // ddr 读取的数据，接输出fifo
    output reg              rd_done             , // 一次写完成标志
    output reg              wr_done               // 一次读完成标志
);
// parameter define
// 调试用，一个英文字母占8个bit
localparam
    IDLE  = 3'b001,
    WRITE = 3'b010,
    READ  = 3'b100;

reg  [  2:0]        cur_state, next_state   ;
reg  [  7:0]        cnt                     ;
reg                 skip                    ;
reg  [ 27:0]        app_addr                ; // 应用地址
reg  [ 27:0]        data_valid_cnt          ;
// wire define
wire                app_en                  ; // 应用使能
wire                app_wdf_wren            ; // 应用写使能
wire [ 15:0]        app_wdf_mask            ; // 应用写数据掩码
wire [  2:0]        app_cmd                 ; // 应用命令，读001，写000
wire [127:0]        app_wdf_data            ; // 应用写数据
wire                app_wdf_end             ; // 应用写数据结束
wire                app_rdy                 ; // 应用就绪
wire                app_wdf_rdy             ; // 应用写数据就绪
wire [127:0]        app_rd_data             ; // 应用读数据
wire                app_rd_data_valid       ; // 应用读数据有效
wire                ui_clk_sync_rst         ; // 用户同步复位

//              main code
// -------------------------------< 组合逻辑 >-------------------------------
// 掩码配置：对应mask_switch 00 01 10---> 0000 00ff ff00
assign app_wdf_mask = (mask_switch == 2'b01) ? 16'h00ff : ((mask_switch == 2'b10) ? 16'hff00 : 16'h0000);
// 读写配置
assign app_cmd = rw ? 3'd1 : 3'd0;
// 数据配置
// assign app_wdf_data = (mask_switch == 2'b01) ? {wr_data[127:64], {64{1'd0}}} : ((mask_switch == 2'b10) ? {{64{1'd0}}, wr_data[63:0]} : wr_data[127:0]);
assign app_wdf_data = wr_data;
// 读写使能配置
assign app_wdf_wren = ((next_state == WRITE) && app_rdy && app_wdf_rdy) ? 1'd1 : 1'd0;
assign app_wdf_end = app_wdf_wren;  // 4:1速率下这两者一致
assign app_en = ((cnt <= burst_data_len) && (((next_state == WRITE) && app_rdy && app_wdf_rdy) || ((next_state == READ) && app_rdy))) ? 1'd1 : 1'd0;
assign input_fifo_rd_en = ((next_state == WRITE) && (cnt <= burst_data_len -16'd1) && app_rdy && app_wdf_rdy) ? 1'd1 : 1'd0;
// 输出配置
assign output_fifo_wr_en = app_rd_data_valid;
assign rd_data = app_rd_data;
// 状态机 状态判断
always @(*)begin
    case (cur_state)
        IDLE :   next_state = skip ? (rw ? READ : WRITE) : IDLE;
        WRITE:   next_state = skip ? IDLE : WRITE;
        READ :   next_state = skip ? IDLE : READ;
        default: next_state = IDLE;
    endcase
end

// -------------------------------< 时序逻辑 >-------------------------------
// 状态机 状态转移
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        cur_state <= IDLE;
    else
        cur_state <= next_state;
end

// 状态机 状态输出
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)begin
        cnt                 <= 8'd0;
        skip                <= 1'd0;
        rd_done             <= 1'd0;
        wr_done             <= 1'd0;
        data_valid_cnt      <= 28'd0;
    end
    else begin
        skip <= 1'd0;
        case (next_state)
            IDLE: begin
                skip <= (init_calib_complete && exc) ? 1'd1 : 1'd0;
                // 每次读写开始前的初始化
                app_addr <= {3'd0, begin_addr};
                rd_done  <= 1'd0;
                wr_done  <= 1'd0;
                // ...
            end
            WRITE: begin 
                if(app_rdy && app_wdf_rdy)begin
                    cnt <= cnt + 8'd1;
                    if(cnt == burst_data_len)begin
                        skip <= 1'd1;
                        wr_done <= 1'd1;
                        cnt <= 8'd0;
                    end
                    else ;
                    // fifo_rd_en从cnt为0时置1，数据会晚一拍，而数据和地址要对齐，因此地址要晚一拍
                    if((cnt >= 8'd1) && (cnt <= burst_data_len - 8'd1))
                        app_addr <= app_addr + 28'd8;
                    else ;
                end
                else ;
            end
            READ: begin
                if(app_rdy)begin
                    if((cnt == burst_data_len - 8'd1) || (cnt == burst_data_len + 8'd1))
                        cnt <= burst_data_len + 8'd1;
                    else 
                        cnt <= cnt + 8'd1;

                    if((cnt >= 8'd0) && (cnt <= burst_data_len - 8'd1))
                        app_addr <= app_addr + 28'd8;
                    else ;
                end
                else ;

                // data_valid_cnt (0 ~ burst_len - 1)
                if(data_valid_cnt == burst_data_len - 28'd1)begin
                    data_valid_cnt <= 28'd0;
                    skip <= 1'd1;
                    rd_done <= 1'd1;
                    cnt <= 8'd0;
                end
                else if(app_rd_data_valid)
                    data_valid_cnt <= data_valid_cnt + 28'd1;
                else ;
            end
        endcase
    end
end

// -------------------------------< mig ip核调用 >-------------------------------
mig_7series_0 u_mig_7series_0 (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr          ),  // 输出 [13:0] DDR3地址
    .ddr3_ba                        (ddr3_ba            ),  // 输出 [2:0] ddr3 Bank地址
    .ddr3_cas_n                     (ddr3_cas_n         ),  // 输出 DDR3列地址选通信号，低电平有效
    .ddr3_ck_n                      (ddr3_ck_n          ),  // 输出 [0:0] DDR3时钟负信号
    .ddr3_ck_p                      (ddr3_ck_p          ),  // 输出 [0:0] DDR3时钟正信号
    .ddr3_cke                       (ddr3_cke           ),  // 输出 [0:0] DDR3时钟使能
    .ddr3_ras_n                     (ddr3_ras_n         ),  // 输出 DDR3行地址选通信号，低电平有效
    .ddr3_reset_n                   (ddr3_reset_n       ),  // 输出 DDR3复位信号，低电平有效
    .ddr3_we_n                      (ddr3_we_n          ),  // 输出 DDR3写使能信号，低电平有效
    .ddr3_dq                        (ddr3_dq            ),  // 双向 [15:0] DDR3数据线
    .ddr3_dqs_n                     (ddr3_dqs_n         ),  // 双向 [1:0] DDR3数据选通负信号
    .ddr3_dqs_p                     (ddr3_dqs_p         ),  // 双向 [1:0] DDR3数据选通正信号
    .init_calib_complete            (init_calib_complete),  // 输出 DDR3初始化和校准完成信号
    .ddr3_cs_n                      (ddr3_cs_n          ),  // 输出 [0:0] DDR3片选信号，低电平有效
    .ddr3_dm                        (ddr3_dm            ),  // 输出 [1:0] DDR3数据掩码
    .ddr3_odt                       (ddr3_odt           ),  // 输出 [0:0] DDR3输出驱动使能

    // Application interface ports
    .app_addr                       (app_addr           ),  // 输入 [27:0] 应用地址
    .app_cmd                        (app_cmd            ),  // 输入 [2:0] 应用命令，读001，写000
    .app_en                         (app_en             ),  // 输入 应用使能
    .app_wdf_data                   (app_wdf_data       ),  // 输入 [127:0] 应用写数据
    .app_wdf_end                    (app_wdf_end        ),  // 输入 应用写数据结束
    .app_wdf_wren                   (app_wdf_wren       ),  // 输入 应用写使能

    .app_rd_data                    (app_rd_data        ),  // 输出 [127:0] 应用读数据
    .app_rd_data_end                (                   ),  // 输出 应用读数据结束
    .app_rd_data_valid              (app_rd_data_valid  ),  // 输出 应用读数据有效
    .app_rdy                        (app_rdy            ),  // 输出 应用就绪
    .app_wdf_rdy                    (app_wdf_rdy        ),  // 输出 应用写数据就绪

    .app_sr_req                     (1'd0               ),  // 输入 应用自刷新请求
    .app_ref_req                    (1'd0               ),  // 输入 应用刷新请求
    .app_zq_req                     (1'd0               ),  // 输入 应用ZQ校准请求

    .app_sr_active                  (                   ),  // 输出 应用自刷新激活
    .app_ref_ack                    (                   ),  // 输出 应用刷新确认
    .app_zq_ack                     (                   ),  // 输出 应用ZQ校准确认
    .ui_clk                         (ui_clk             ),  // 输出 用户界面时钟
    .ui_clk_sync_rst                (ui_clk_sync_rst    ),  // 输出 用户界面时钟同步复位，高电平有效

    .app_wdf_mask                   (app_wdf_mask       ),  // 输入 [15:0] 应用写数据掩码

    // System Clock Ports
    .sys_clk_i                      (clk_200m           ),  // 输入 系统时钟
    .sys_rst                        (sys_rst_n          )   // 输入 系统复位（低电平有效）
);
endmodule
